package com.aepryus.loom;

import java.util.*;
import com.aepryus.util.*;

public class Runner {
	static final String transKey = "transKey";

	private static long lastJobID = 0;
	private static List<Job> jobs = new Vector<Job>();
	private static List<Job> completed = new Vector<Job>();

	private static long lastTaskID = 0;
	private static Map<Thread,Task> tasks = Collections.synchronizedMap(new HashMap<Thread,Task>());

	private static Credentials getCredentials () {
		// Hack: Fix later when implementing security for the API
		try {
			return (Credentials)Controller.controller.getTracker().getSession().getAttribute("credentials");
		} catch (NullPointerException e) {
			Credentials credentials = new Credentials();
			credentials.setUsernameID(0);
			credentials.setUsername("api");
			credentials.setName("api");
			return credentials;
		}
	}

// Job =========================================================================
	public static synchronized void submit (Job job) {
		if (job.getJobID() != 0) throw new RuntimeException("This job has already been submitted.  Jobs can not be submitted more than once.  Please instantiate an new instance in order to resubmit");
		job.setJobID(lastJobID++);
		job.setUsername(getCredentials().getUsername());
		jobs.add(job);
		job.setPriority(4);
		job.ok();
		job.start();
	}
	static synchronized void complete (Job job) {
		jobs.remove(job);
		completed.add(job);
	}

	public synchronized static Job[] getJobs () {
		return jobs.toArray(new Job[]{});
	}
	public synchronized static Job[] getCompleted () {
		return completed.toArray(new Job[]{});
	}

// Task ========================================================================
	public static Object submit (Task task) throws ValidationException,PersistenceException {
		int attempts = 0;
		Object domain;

		task.setStartTime(new Date());
		task.setUsername(getCredentials().getUsername());
		addTask(task);
		while (true) {
		    try {
				domain = task.process();
				task.persist();
				removeTask();
				break;

		    } catch (LockException e) {
				task.unwind();
				attempts++;
				if (attempts > 30)
				    throw new RuntimeException("Potential Lock Loop");

				Log.print("Lock Attempt [ "+attempts+" ] on Domain ["+e.getDomainName()+":"+e.getDomainID()+"] Failed");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException ie) {/**/}

			} catch (ValidationException e) {
				task.unwind();
				removeTask();
				throw e;
			} catch (PersistenceException e) {
				task.unwind();
				removeTask();
				throw e;
		    } catch (RuntimeException e) {
				task.unwind();
				removeTask();
				throw e;
			// In general the potential of bugs related to simple and useful recursive
			// functions, makes me believe that this Error should have been a RuntimeException.
			// Since it is not, I am catching it here as if it were a RuntimeException.
			// jjc : 4/1/2004
			} catch (StackOverflowError e) {
				task.unwind();
				removeTask();
				throw new RuntimeException("StackOverflowError ("+e.getMessage()+")");
		    }
		}

		return domain;
	}

	static void addTask (Task task) {
		synchronized (transKey) {
		    if (getTask() != null) throw new RuntimeException("Tasks can not be nested.");
		    tasks.put(Thread.currentThread(),task);
		    task.setTaskID(lastTaskID++);
		}
	}
	static Task getTask () {
		synchronized (transKey) {
			return tasks.get(Thread.currentThread());
		}
	}
	static void removeTask () {
		synchronized (transKey) {
			tasks.remove(Thread.currentThread());
		}
	}
	public static Task[] getTasks () {
		synchronized (transKey) {
			List<Task> list = new Vector<Task>();
			for (Thread thread : tasks.keySet())
				list.add(tasks.get(thread));
			return list.toArray(new Task[]{});
		}
	}
	public static int taskCount () {
		synchronized (transKey) {
			return tasks.size();
		}
	}
}

